-- ======================================================================
--  Hell Watch Lua IO App
-- 
-- Copyright 2014 Hell-Prototypes
--
-- http://www.hellprototypes.com
--
-- This is free software, licensed under the terms of the GNU General
-- Public License as published by the Free Software Foundation.
-- ======================================================================
spi = require "mod_fw_spi"
iHEX = require "mod_iHEX"
printf = require "mod_print"

-- Define ----------------------------------------------------------------------
FLASH_PAGE_SIZE = 32
FLASH_PAGE_NUM  = 64

Default_file_name = "Hell_nRF2401P.hex"

-- SPI pin index define --------------------------------------------------------
SPI_MOSI_PIN = 3
SPI_MISO_PIN = 2
SPI_SCK_PIN  = 1
SPI_SS_PIN   = 0

function isp_init()
	if not spi.init(SPI_MOSI_PIN, SPI_MISO_PIN, SPI_SCK_PIN, SPI_SS_PIN) then
		print("<ISP> spi.io_init fail")
		return false
	end

	spi.pin_reset(SPI_SS_PIN)
	spi.pin_reset(SPI_SCK_PIN)
	spi.pin_reset(SPI_MOSI_PIN)
	spi.pin_set(SPI_SS_PIN)
	spi.pin_reset(SPI_SS_PIN)
	spi.ms_delay(25)

	return true
end
-- -----------------------------------------------------------------------------
function CMD_Poll_Busy()
	local busy
	local wait = 20
	
	while wait > 0 do
		wait = wait - 1
		busy = spi.send_32bit(0xf0000000)
		--print(string.format("<spi> busy = 0x%x", busy))
		if not bit32.btest(busy, 1) then
			return true
		end
		spi.ms_delay(1)
		--io.write(".")
	end
	
	return false
end
-- -----------------------------------------------------------------------------
function CMD_Programming_Enable()
	spi.send_32bit(0xAC530000)
end
-- -----------------------------------------------------------------------------
function CMD_Read_Fuse_Bytes()
	local low_byte, high_byte

	low_byte  = spi.send_32bit(0x50000000)
	high_byte = spi.send_32bit(0x58080000)
	
	return high_byte*256 + low_byte
end
-- -----------------------------------------------------------------------------
function CMD_Read_Signature_Byte()
	local sb0, sb1, sb2

	sb0 = spi.send_32bit(0x30000000)
	sb1 = spi.send_32bit(0x30000100)
	sb2 = spi.send_32bit(0x30000200)

	return 	sb2*0x10000 + sb1*0x100 + sb0
end
-- -----------------------------------------------------------------------------
function CMD_Chip_Erase()
	spi.send_32bit(0xAC800000)
	return CMD_Poll_Busy()
end
-- -----------------------------------------------------------------------------
function CMD_Load_One_Word(data, word_addr)
	local cmd0 = 0x40000000 + bit32.lshift(word_addr, 8) + bit32.band(data, 0xFF)
	local cmd1 = 0x48000000 + bit32.lshift(word_addr, 8) + bit32.rshift(bit32.band(data, 0xFF00), 8)
	--print(string.format("<ISP> cmd0 = 0x%x, cmd1 = 0x%x", cmd0, cmd1))
	spi.send_32bit(cmd0)
	spi.send_32bit(cmd1)
end
-- -----------------------------------------------------------------------------
function CMD_Load_One_Page(t_page_data, page_num)
	if #t_page_data < 2 then
		return false
	end

	local word_data = 0
	local data_index = 1
	--print("============= " .. page_num .. " =============")
	for word_addr = 0, #t_page_data/2 - 1 do
		word_data = bit32.band(t_page_data[data_index], 0xFF) + bit32.band(t_page_data[data_index + 1], 0xFF)*0x100
		data_index = data_index + 2
		CMD_Load_One_Word(word_data, word_addr)
	end
	
	return true
end
-- -----------------------------------------------------------------------------
function CMD_Write_One_Page(t_page_data, page_num)
	if page_num >= FLASH_PAGE_NUM then
		return false
	end
	if not CMD_Load_One_Page(t_page_data, page_num) then
		return false
	end

	local cmd = 0x4C000000 + bit32.lshift(page_num, 12)
	--print(string.format("<ISP> write page cmd = 0x%x", cmd))
	spi.send_32bit(cmd)

	return CMD_Poll_Busy()
end
-- -----------------------------------------------------------------------------
function CMD_Program_Flash(start_address, t_program_data)
	local page_num = math.floor(start_address/FLASH_PAGE_SIZE)
	local t_page_data={}
	local t_page_data_index = 1

	io.write("<ISP> " .. math.floor(#t_program_data/FLASH_PAGE_SIZE) .. " Page to update: \r\n")
	for i=1, #t_program_data do
		t_page_data[t_page_data_index] = t_program_data[i]
		t_page_data_index = t_page_data_index + 1
		if (t_page_data_index > FLASH_PAGE_SIZE) or ( i == #t_program_data) then
			local percent = math.floor((1000*i)/#t_program_data)
			io.write(string.format("\r\tUpdate page: %d,\tProgress: %%%0.1f\t", page_num, percent/10))
			-- print("===============Write page: " .. page_num .. " ===============")
			if not CMD_Write_One_Page(t_page_data, page_num) then
				print("")
				return false
			end
			page_num = page_num + 1
			t_page_data = {}
			t_page_data_index = 1
		end
	end

	print("")
	return true
end
-- -----------------------------------------------------------------------------
function CMD_Program_EEPROM(start_address, t_program_data)
    local cmd
    local addr

	io.write("<ISP> " .. #t_program_data .. " Byte will be write to EEPROM: ")
	for i=1, #t_program_data do
        addr = (start_address + i - 1) * 0x100
        cmd = 0xC0000000 + addr + t_program_data[i]
    	--print(string.format("<ISP> write eeprom cmd = 0x%x", cmd))
    	spi.send_32bit(cmd)
    	if not CMD_Poll_Busy() then
    		return false
    	end
	end

	print("")
	return true
end
-- -----------------------------------------------------------------------------
function CMD_Read_Program(start_address, data_len)
	local word_len = math.floor(data_len/2)
end
-- -----------------------------------------------------------------------------
function ISP_Program_Flash(pgm_hex_file)
	local pgm_raw_data = {}
	local pgm_start_addr = 0
	
	local f=io.open(pgm_hex_file,"r")
   	if f~=nil then
		io.close(f)
	else
		print("<ISP> Error: Program file not exist, file name: " .. pgm_hex_file)
		return
	end
	
	pgm_start_addr, pgm_raw_data = iHEX.Read_File(pgm_hex_file)

	if CMD_Chip_Erase() then
		print("<ISP> Chip Erase Done ...")
		if CMD_Program_Flash(pgm_start_addr, pgm_raw_data) then
			print("<ISP> Program Flash DONE ...")
		else
			print("<ISP> Error: CMD_Program_Flash fail")
		end
		
	else
		print("<ISP> Error: CMD_Chip_Erase fail")
	end
end

-- -----------------------------------------------------------------------------
function main(arg_flash_file_name)
	if not spi.device_open() then
		print("<ISP> Open USB interface fail")
		return
	end

	if not isp_init() then
		print("<ISP> isp_init fail")
	end

	CMD_Programming_Enable()
	
	signature = CMD_Read_Signature_Byte()
	print(string.format("<ISP> signature = 0x%x", signature))
	
	if signature == 0xb911e then
		if arg_flash_file_name ~= nil then
			ISP_Program_Flash(arg_flash_file_name)
		else
			ISP_Program_Flash(Default_file_name)
		end
	else
		print("Signature error, not a ATtiny24A device")
	end

	spi.free()
	if not spi.device_close() then
		print("<ISP> Close USB interface fail")
	end
end

main(arg[1])
